package com.nulldev.util.uri.yt.extraction;

import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.regex.Pattern;

import com.nulldev.util.java.DataSet;
import com.nulldev.util.java.DataTriplet;
import com.nulldev.util.java.JVMUtil;
import com.nulldev.util.java.date.DateUtil;
import com.nulldev.util.lang.json.Json;
import com.nulldev.util.lang.json.JsonObject;
import com.nulldev.util.math.MathUtil;
import com.nulldev.util.other.ArrayUtils;
import com.nulldev.util.other.BooleanUtil;
import com.nulldev.util.string.StringsUtil;
import com.nulldev.util.uri.URIUtil;
import com.nulldev.util.uri.Soup_imported.Jsoup;
import com.nulldev.util.uri.Soup_imported.nodes.Document;
import com.nulldev.util.uri.Soup_imported.nodes.Element;
import com.nulldev.util.uri.Soup_imported.select.Elements;

public class ChannelExtractor {
	
	public static class Channel {
		private final LinkedList<Video> videos;
		private final String channelName;
		private final Elements metaTags;
		private final JsonObject channelImages;
		private String channelID;
		private final long subscribers;
		
		public Channel(final LinkedList<Video> videos, 
				final String channelName, final Elements metaTags,
				final JsonObject channelImages, final long subscribers) {
			this.videos = videos;
			this.channelName = channelName;
			this.channelImages = channelImages;
			this.metaTags = metaTags;
			this.subscribers = subscribers;
			boolean _processFF = false, _processPaid = false, _processCID = false;
			try {
				for (final Element ele : metaTags) {
					try {
						if (ele.attr("itemprop").equals("isFamilyFriendly") && !_processFF) {
							this.familyFriendly = BooleanUtil.safeParse(ele.attr("content"));
							_processFF = true;
						}else if (ele.attr("itemprop").equals("paid") && !_processPaid) {
							this.paidContent = BooleanUtil.safeParse(ele.attr("content"));
							_processPaid = true;
						}else if (ele.attr("itemprop").equals("channelId") && !_processCID) {
							this.channelID = ele.attr("content");
							_processCID = true;
						}
					} catch (Exception e) {
					}
				}
			}catch (Exception e) {
			}
		}
		
		public List<Video> getVideos() {
			return Collections.unmodifiableList(videos);
		}
		
		public String getChannelName() {
			return this.channelName;
		}
		
		private boolean familyFriendly, paidContent;
		
		/**
		 * ????????? <br>
		 * This is a meta tag for youtube channels.
		 * @returns boolean
		 */
		public boolean isFamilyFriendly() {
			return this.familyFriendly;
		}
		
		public boolean hasPaidContent() {
			return this.paidContent;
		}

		public JsonObject getChannelImages() {
			return this.channelImages;
		}
		
		public String getChannelID() {
			return this.channelID;
		}
		
		public long getSubscriberCount() {
			return this.subscribers;
		}
	}

	public static class Video {
		private String title;
		private String url;
		private String thumbnail;
		private String videoID;
		private long duration;
		
		public Video(final String title, final String videoID, final long duration) {
			this.title = title;
			this.url = "/watch?v=" + videoID;
			this.thumbnail = "http://i.ytimg.com/vi/" + videoID + "/mqdefault.jpg";
			this.videoID = videoID;
			this.duration = duration;
		}
		
		public String getTitle() {
			return this.title;
		}
		
		public String getURL() {
			return this.url;
		}
		
		public String getThumbURL() {
			return this.thumbnail;
		}

		public String getID() {
			return this.videoID;
		}
		
		public long duration() {
			return this.duration;
		}
	}
	
	public static Channel extract(String channelID) {
		boolean isUser = false;
		if (channelID.startsWith("/user/")) {
			channelID = channelID.substring(1);
			isUser = true;
		}
		final LinkedList<Video> videos = new LinkedList<Video>();
		String channelName = "$channel.name";
		final String URL = (isUser ? "https://www.youtube.com/" + channelID + "/videos?shelf_id=0&sort=dd&view=0"
				: "https://www.youtube.com/channel/" + channelID + "/videos?shelf_id=0&sort=dd&view=0");
		final String result = URIUtil.downloadPageS(URIUtil.safeCreate(URL));
		final Document doc = Jsoup.parse(result);
		final Elements videosParsed = doc.getElementsByClass("yt-lockup-title"); //"yt-uix-sessionlink yt-uix-tile-link  spf-link  yt-ui-ellipsis yt-ui-ellipsis-2");
		final Elements timeStamps = doc.getElementsByClass("video-time");
		int i = 0;
		for (final Element video : videosParsed) {
			final Element videoOrg = video.child(0);
			final String[] tsp = videoOrg.attr("title").split("-");
			String title = (tsp.length == 1 ? tsp[0] : StringsUtil.join(ArrayUtils.to(tsp, tsp.length - 1), "-"));
			if (title.endsWith("  ")) {
				title = title.substring(0, title.length() - 2);
			}
			final String link = videoOrg.attr("href");
			final String id = link.split(Pattern.quote("/watch?v="))[1];
			long dur = -1;
			try {
				if (i >= timeStamps.size()) {
					i -= 1;
				}
				dur = MathUtil.TimeToSeconds.parseTime(timeStamps.get(i).child(0).html());
				i += 1;
			}catch (Exception e) {
				e.printStackTrace();
				i += 1;
			}
			videos.add(new Video(title, id, dur));
		}
		for (final Element meta : doc.getElementsByTag("meta")) {
			try {
				if (meta.attr("name").equals("title")) {
					channelName = meta.attr("content");
					break;
				}
			}catch (Exception e) {}
		}
		final JsonObject channelImages = Json.object();
		final Element videoBanner = doc.getElementById("gh-banner").child(0);
		final String x = StringsUtil.substringBetweenO(videoBanner.toString(), "#c4-header-bg-container {      background-image: url(//",
				");  }");
		channelImages.add("standardBanner", "https://" + 
				x);
		final String x1 = StringsUtil.substringBetweenO(videoBanner.toString(), 
				"#c4-header-bg-container .hd-banner-image {      background-image: url(//",
				");  }");
		channelImages.add("qualityBanner", "https://" + 
				x1);
		final String x2 = StringsUtil.substringBetweenO(result, "<img class=\"appbar-nav-avatar\" src=\"", "\" ");
		channelImages.add("channel", x2);
		long subscribers = -1;
		try {
			final Elements candid = doc.getElementsByClass("subscribed");
			if (!candid.isEmpty()) {
				subscribers = MathUtil.safeParseL(
						candid.get(0).html().replaceAll(Pattern.quote(" "), "")
						.replaceAll(Pattern.quote("&nbsp;"), ""), -1);
			}else {
				//probably a VEVO/Private account
			}
		}catch (Exception e) {
			e.printStackTrace();
		}
		return new Channel(videos, channelName, doc.getElementsByTag("meta"), channelImages, subscribers);
	}
}
